package jwtx

// @Description
// @Author 代码小学生王木木
// @Date 2024-04-03 12:10

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/redis/go-redis/v9"
	"strings"
	"time"
)

var (
	AtKey = []byte("95osj3fUD7fo0mlYdDbncXz4VD2igvf0")
	RtKey = []byte("95osj3fUD7fo0mlYdDbncXz4VD2igvfx")
)
var (
	NotLogin          = errors.New("用户未登录")
	TokenExpire       = errors.New("Token已过期")
	UserAgentMismatch = errors.New("用户代理信息不一致")
)

type RedisJwtHandler struct {
	cmd redis.Cmdable
}

func NewRedisJWTHandler(cmd redis.Cmdable) JwtHandler {
	return &RedisJwtHandler{cmd: cmd}
}

func (r *RedisJwtHandler) queryFromHeader(ctx *gin.Context) string {
	tokenHeader := ctx.GetHeader("Authorization")
	//Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
	segment := strings.Split(tokenHeader, " ")
	if len(segment) != 2 {
		return ""
	}
	return segment[1]
}

func (r *RedisJwtHandler) queryFromURL(ctx *gin.Context) string {
	return ctx.Query("token")
}

func (r *RedisJwtHandler) queryFromFormData(ctx *gin.Context) string {
	return ctx.PostForm("token")
}

func (r *RedisJwtHandler) getKey(uid int64) string {
	return fmt.Sprintf("login_hold:%d", uid)
}

func (r *RedisJwtHandler) ExtractToken(ctx *gin.Context) string {
	var tokenStr string
	if tokenStr = r.queryFromHeader(ctx); len(tokenStr) != 0 {
		return tokenStr
	}
	if tokenStr = r.queryFromURL(ctx); len(tokenStr) != 0 {
		return tokenStr
	}
	if tokenStr = r.queryFromFormData(ctx); len(tokenStr) != 0 {
		return tokenStr
	}
	return tokenStr
}

func (r *RedisJwtHandler) GetJWTToken(ctx *gin.Context, uid int64) (string, error) {
	claim := UserClaims{
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
		},
		Id: uid,
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, claim)
	tokenStr, err := token.SignedString(AtKey)
	if err != nil {
		return "", err
	}
	r.cmd.Set(ctx, r.getKey(uid), tokenStr, time.Hour*24)
	return tokenStr, nil
}

func (r *RedisJwtHandler) ParseToken(ctx *gin.Context, tokenStr string) (UserClaims, error) {
	// 同个包内的方法，结构体可以直接调用
	claim := UserClaims{}
	token, err := jwt.ParseWithClaims(tokenStr, &claim, func(token *jwt.Token) (interface{}, error) {
		return []byte(AtKey), nil
	})
	if err != nil {
		return UserClaims{}, NotLogin
	}
	if claim.ExpiresAt.Time.Before(time.Now()) {
		return UserClaims{}, TokenExpire
	}
	if claim.UserAgent != ctx.Request.UserAgent() {
		return UserClaims{}, UserAgentMismatch
	}
	if token == nil || !token.Valid || claim.Id <= 0 {
		return UserClaims{}, NotLogin
	}
	return claim, nil
}
